Skip to content

Adding full support for custom materials to GltfLoader#2725

Open
theMinka wants to merge 14 commits into
jMonkeyEngine:masterfrom
theMinka:theMinka_Custom-material-support-in-GltfLoader
Open

Adding full support for custom materials to GltfLoader#2725
theMinka wants to merge 14 commits into
jMonkeyEngine:masterfrom
theMinka:theMinka_Custom-material-support-in-GltfLoader

Conversation

@theMinka
Copy link
Copy Markdown
Contributor

Reworked the material creation process in GltfLoader, to allow for custom materials to be created.

Supporting a custom material definition

To support a new material definition, one simply needs to implement a new GltfMaterialFactory and register it with the GltfLoader. It is important to consider the overall order of all registered material factories, because only the first factory that accepts a given material data instance will be used. By default, there are already two material factories: one for the PBRLighting material and one for the Unshaded material.

One special use case would be when someone creates a copy of the standard PBRLighting material to add additional functionality. If none of the original material parameters have been changed (adding new ones is not a problem), one can simply extend PBRLightingMaterialFactory and modify the material definition path returned by the getMaterialDefPath() method. Finally, the original PBRLightingMaterialFactory just needs to be replaced in the GltfLoader with the custom one. This way, all loaded models will use the extended PBRLighting material.

Backward compatibility

As mentioned earlier, my new system is not backward compatible with regard to custom MaterialAdapters. Therefore, I have kept the old implementation but disabled it. To reactivate it, a specific flag must be set in the GltfModelKey. This will fully switch back to the old material creation process when loading the corresponding 3D model.

However, I believe that keeping the old MaterialAdapter system in the engine is not a good long-term solution. I suggest removing it in one or two future releases. To reflect this, I have already marked most classes, methods, and fields related to the old process as deprecated.

Forum discussion:
https://hub.jmonkeyengine.org/t/adding-full-support-for-custom-materials-to-gltfloader/49444

Also fixed some related bugs:

  • CustomContentManager can now handle multiple GLTF extensions per element
  • Wrong material parameter was set on unlit materials with vertex colors (UseVertexColor instead of VertexColor)
  • Materials using BlendMode.AlphaAdditive are now added to QueueBucket.Transparent

If multiple GLTF extensions are present on an element, the CustomContentManager now processes all supported ones instead of just the first one.
* Step 1: Collect all material data in the new GltfMaterialData object
* Step 1.1: GltfLoader reads all standard GLTF parameters
* Step 1.2: ExtensionLoader and ExtrasLoader can read additional GLTF parameters
* Step 2: Find a matching GltfMaterialFactory and use it to create the material
…ensionLoaders

* Retains the old MaterialAdapter handling for backward compatibility
…d material

* Added both material factories as the default ones to the GltfLoader
Reason: Setting default values directly in GltfMaterialData obscures whether a parameter is actually defined in the glTF material. The presence of material parameters may be relevant for some material factories. Therefore, default values should be set by the material factories themselves.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new material factory system for the GLTF loader, deprecating the legacy material adapter system. The new architecture uses GltfMaterialData and GltfMaterialFactory to provide a more extensible approach to material creation. Feedback was provided to correct a typo in the EMISSIV constant names within GltfMaterialData.java to improve code consistency.

Comment thread jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialData.java Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR reworks glTF material creation in GltfLoader by introducing a GltfMaterialFactory pipeline backed by a new GltfMaterialData container, enabling custom material implementations while keeping the legacy MaterialAdapter system as an opt-in fallback for backward compatibility.

Changes:

  • Added a factory-based material creation system (GltfMaterialFactory, GltfMaterialData) with default factories for PBRLighting and Unshaded (KHR_materials_unlit).
  • Updated extension loaders to populate GltfMaterialData (and adjusted CustomContentManager to process multiple extensions per element).
  • Deprecated legacy MaterialAdapter-based classes/entry points and added a GltfModelKey flag to re-enable the legacy path.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnshadedMaterialFactory.java New factory that creates Unshaded materials from GltfMaterialData (KHR_materials_unlit).
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRLightingMaterialFactory.java New factory that creates PBRLighting materials (including spec/gloss extension support).
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialFactory.java New SPI interface for pluggable material creation (contains a small Javadoc typo).
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfMaterialData.java New material-data container and parameter naming conventions for factories/extensions.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfLoader.java Integrates factory pipeline, adds registration APIs, adjusts transparency bucketing, and adds legacy fallback switch.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/CustomContentManager.java Fix: process multiple extensions for the same element instead of returning after the first.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitExtensionLoader.java Updated to tag GltfMaterialData with unlit extension; legacy adapter path retained/deprecated.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossExtensionLoader.java Updated to populate spec/gloss parameters into GltfMaterialData; legacy path retained/deprecated.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthExtensionLoader.java Updated to populate emissive strength into GltfMaterialData; legacy path retained/deprecated.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfModelKey.java Adds flag to re-enable legacy adapters and deprecates adapter APIs (needs cache-key equality update).
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java Adds helper to read the legacy-adapter-enabled flag; deprecates adapter accessor.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/MaterialAdapter.java Deprecated as part of migration path.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMaterialAdapter.java Deprecated as part of migration path.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRMetalRoughMaterialAdapter.java Deprecated as part of migration path.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBRSpecGlossMaterialAdapter.java Deprecated as part of migration path.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/PBREmissiveStrengthMaterialAdapter.java Deprecated as part of migration path.
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/UnlitMaterialAdapter.java Deprecated as part of migration path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


setParam(material, "EmissiveMap", gltfMaterialData.getGltfParam(EMISSIVE_TEXTURE_PARAM));
setParam(material, "Emissive", gltfMaterialData.getGltfParam(EMISSIVE_COLOR_PARAM), ColorRGBA.Black);
setParam(material, "EmissiveIntensity", gltfMaterialData.getGltfParam(EMISSIVE_STRENGTH_PARAM));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default value of 1 was added to "EmissiveIntensity" parameter.

@@ -526,9 +535,10 @@ public Geometry[] readMeshPrimitives(int meshIndex) throws IOException {
geom.setMaterial(defaultMat);
Copy link
Copy Markdown
Contributor Author

@theMinka theMinka May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vertex coloring flag is now also set correctly for the default material.

Comment on lines +811 to +815
public Material readMaterial(int materialIndex, boolean usesVertexColors) throws IOException {
// Fallback to the old material adapter system, if the legacy flag is set.
if (GltfUtils.isMaterialAdaptersEnabled(info)) {
return readMaterialUsingMaterialAdapters(materialIndex);
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vertex coloring flag is now also set correctly in the legacy system.

Comment on lines +55 to +63
/**
* Enables or disables the legacy material adapter system.
* This should only be used in older projects for backward compatibility.
*/
private boolean materialAdaptersEnabled = false;

@Deprecated
private Map<String, MaterialAdapter> materialAdapters = new HashMap<>();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

materialAdaptersEnabled flag has been added to the equals() and hashCode() method.

/**
* A material factory creates {@link Material}s based on the data of a single material from a GLTF file.
* <p>
* All material factories have bo be registered at the {@link GltfLoader} by using one of its
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo has been fixed.

Comment on lines +1805 to +1806
unregisterMaterialFactory(materialFactory.getClass());
materialFactoryList.add(0, materialFactory);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation has been fixed.

Comment on lines +811 to +823
public Material readMaterial(int materialIndex, boolean usesVertexColors) throws IOException {
// Fallback to the old material adapter system, if the legacy flag is set.
if (GltfUtils.isMaterialAdaptersEnabled(info)) {
return readMaterialUsingMaterialAdapters(materialIndex);
}

assertNotNull(materials, "There is no material defined yet a mesh references one");
JsonObject materialJson = materials.get(materialIndex).getAsJsonObject();

GltfMaterialData gltfMaterialData = readStandardMaterialParameters(materialJson);
gltfMaterialData.setHasVertexColors(usesVertexColors);
gltfMaterialData = customContentManager.readExtensionAndExtras("material", materialJson, gltfMaterialData);
return createMaterial(gltfMaterialData, materialIndex);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added many new tests for various material parameter combinations to the GltfLoaderTest.

@theMinka
Copy link
Copy Markdown
Contributor Author

I noticed that PBR materials using the "KHR_materials_emissive_strength" extension were broken. This was caused by the legacy part of the PBREmissiveStrengthExtensionLoader, which always replaced the previous MaterialAdapter with its own. Unfortunately, the PBREmissiveStrengthMaterialAdapter did not handle any parameters of the default metallic-roughness system, so those parameters were lost after the replacement. Therefore, I fixed the old implementation by making it additive instead of replacing the existing adapter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants